package com.lp.gps_alarm;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Schnittstelle zum Backend. Ruft Backend mit HTTP-Funktionen auf.
 * 
 * @author Daniel Fritz
 * @version 1.0
 */
public class ServerAccess_GPS_Alarm {

	private int userID;
	private final String serverAdress = "http://evening-sierra-8959.herokuapp.com/";
	// private final String serverAdress = "http://10.0.2.2:3000/";

	private ArrayList<Poi> poiList = new ArrayList<Poi>();

	/**
	 * Konstruktor. Initialisiert userID auf -1.
	 * 
	 */
	public ServerAccess_GPS_Alarm() {
		userID = -1;
	}

	/**
	 * 
	 * prüft, ob es in der Datenbank eine Übereinstimmung von ‚user‘ und ‚pw‘
	 * gibt. Falls nicht oder falls die Verbindung fehlschlägt, wird false
	 * zurückgegeben, ansonsten true.
	 * 
	 * @param User
	 *            -emailadresse
	 * @param User
	 *            -password
	 * @return Rückgabe ob erfolgreich
	 * 
	 */
	public boolean login(String user, String pw) throws JSONException {

		boolean loginBool = false;
		int login = -1;
		login = findUser(user, pw, true);

		if (login == -1) {
			System.out.println("login request failed/no connection!");
		} else if (login == 0) {
			System.out.println("user name or password incorrect!");
		} else {
			System.out.println("password correct, login successful!");
			userID = login;
			loginBool = true;
		}
		return loginBool;
	}

	/**
	 * 
	 * setzt die ‚userID‘ auf -1, sodass nicht mehr auf Daten zugegriffen werden
	 * kann.
	 * 
	 */
	public void logout() {
		userID = -1;
		System.out.println("logout successful!");
	}

	/**
	 * 
	 * prüft als erstes, ob der Benutzer schon existiert, denn er soll nicht
	 * mehrfach angelegt werden. Falls nicht, wird ein httpPost-request mit dem
	 * entsprechenden JSON-Objekt an die Datenbank gesendet. Bei Erfolg wird
	 * true zurückgegeben, ansonsten false.
	 * 
	 * @param User
	 *            -emailadresse
	 * @param User
	 *            -password
	 * @return Rückgabe ob erfolgreich
	 * 
	 */
	public boolean register(String user, String pw) throws JSONException,
			ClientProtocolException, IOException {
		JSONObject data = new JSONObject();
		boolean regBool = false;

		if (findUser(user, pw, false) == 1) {
			System.out.println("User already exists!");
			return regBool;
		}

		data.put("utf8", "V");
		data.put("authenticity_token",
				"h3f1svg/XvyxrC7Be4yiC1nD90Y1YM47WUpLVx1fiF0=");
		JSONObject userData = new JSONObject();
		userData.put("Email", user);
		userData.put("Password", pw);
		data.put("user", userData);
		data.put("commit", "Create User");

		StringEntity se = new StringEntity(data.toString());

		if (httpPost(se, serverAdress + "users.json")) {
			System.out.println("registered successfully!");
			regBool = true;
		} else {
			System.out.println("failed to register");
		}
		return regBool;
	}

	/**
	 * searches for User 'user'. If no PW-check was requested it either returns
	 * 1 if user exists or 0 if not. If PW-check was requested, it returns the
	 * id of the user if he exists and the PW is correct, 0 if PW or user name
	 * is not correct -1 if the request failed
	 */
	private int findUser(String user, String pw, boolean checkPW)
			throws JSONException {
		JSONArray jsonArray = httpGetMultiple(serverAdress + "users.json");

		if (jsonArray == null) {
			System.out.println("request failed!");
			return -1;
		}

		for (int i = 0; i < jsonArray.length(); i++) {
			JSONObject jsonObject;
			jsonObject = jsonArray.getJSONObject(i);

			if (jsonObject.getString("Email").equals(user)) {
				if (!checkPW) {
					return 1;
				}
				if (jsonObject.getString("Password").equals(pw)) {
					return jsonObject.getInt("id");
				} else {
					return 0;
				}
			}
		}
		return 0;

	}

	/**
	 * 
	 * lädt alle POIs des eingeloggten Benutzers. Wenn kein Benutzer eingeloggt
	 * ist oder die Verbindung fehlschlägt, wird vorzeitig abgebrochen.
	 * 
	 * @return Liste mit Zielpunkten
	 * 
	 */
	public ArrayList<Poi> loadPOIs() throws JSONException {

		if (poiList != null) {
			poiList.removeAll(poiList);
		}
		if (this.userID == -1) {
			System.out.println("Please login or register first.");
			return null;
		}
		JSONArray allPois = httpGetMultiple(serverAdress + "pois.json");
		if (allPois == null) {
			System.out.println("could not load POIs");
			return null;
		}
		JSONArray pois = new JSONArray();

		for (int i = 0; i < allPois.length(); i++) {

			if (allPois.getJSONObject(i).getInt("user_id") == this.userID) {
				pois.put(allPois.getJSONObject(i));
			}
		}

		for (int i = 0; i < pois.length(); i++) {
			Poi myPoi = new Poi();
			myPoi.setDescription(pois.getJSONObject(i).getString("Description"));
			myPoi.setxDestinationCoordinate(pois.getJSONObject(i).getDouble(
					"XCoordinate"));
			myPoi.setyDestinationCoordinate(pois.getJSONObject(i).getDouble(
					"YCoordinate"));
			myPoi.setRadius(pois.getJSONObject(i).getDouble("Radius"));
			poiList.add(myPoi);
		}

		return poiList;
	}

	/**
	 * 
	 * falls ein User eingeloggt ist, wird der POI als JSON-Objekt. Wenn
	 * erfolgreich, wird true zurückgegeben, ansonsten false.
	 * 
	 * @param Radius
	 *            des Zielbereichs
	 * @param x
	 *            -Koordinate
	 * @param y
	 *            -Koordinate
	 * @param Beschreibung
	 *            des Weckpunktes
	 * @return Rückgabe ob erfolgreich
	 * 
	 */
	public boolean savePoi(double radius, double x, double y, String description)
			throws JSONException, ClientProtocolException, IOException {

		if (userID == -1) {
			System.out.println("You have to register or login first");
			return false;
		}

		JSONObject data = new JSONObject();

		data.put("utf8", "V");
		data.put("authenticity_token",
				"h3f1svg/XvyxrC7Be4yiC1nD90Y1YM47WUpLVx1fiF0=");
		JSONObject poiData = new JSONObject();
		poiData.put("Radius", radius);
		poiData.put("XCoordinate", x);
		poiData.put("YCoordinate", y);
		poiData.put("Description", description);
		poiData.put("user_id", this.userID);
		data.put("poi", poiData);
		data.put("commit", "Create Poi");

		StringEntity se = new StringEntity(data.toString());

		if (httpPost(se, serverAdress + "pois.json")) {
			System.out.println("POI saved successfully!");
			return true;
		} else {
			System.out.println("failed to save POI");
			return false;
		}
	}

	/**
	 * 
	 * sendet ein HttpPost-request an die Datenbank (und innerhalb der Datenbank
	 * an die Adresse ‚path‘). Bei Erfolg wird true zurückgegeben, ansonsten
	 * false.
	 * 
	 * @param Message
	 * @param Pfad
	 * @return Rückgabe ob erfolgreich
	 * 
	 */
	private boolean httpPost(StringEntity message, String path)
			throws ClientProtocolException, IOException {
		// prepare JSONObject
		DefaultHttpClient httpclient = new DefaultHttpClient();
		HttpPost httpPost = new HttpPost(path);
		// senden
		httpPost.setEntity(message);
		httpPost.setHeader("Accept", "application/json");
		httpPost.setHeader("Content-type", "application/json");

		HttpResponse response = httpclient.execute(httpPost);
		int code = response.getStatusLine().getStatusCode();

		if (code == 201) {
			return true;
		} else {
			return false;
		}

	}

	/**
	 * 
	 * sendet (über die Funktion ‚httpGet‘) ein httpGet-request an die
	 * Datenbank. Bei Erfolg wird aus dem String ein JSON-Array erstellt und
	 * zurückgegeben, ansonsten ‚null‘.
	 * 
	 * @param Pfad
	 * @return JSON Datei
	 * 
	 */
	private JSONArray httpGetMultiple(String path) {
		try {
			String readJson = httpGet(path);
			JSONArray jsonArray = new JSONArray(readJson);
			return jsonArray;

		} catch (JSONException e) {
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return null;
	}

	/**
	 * 
	 * sendet ein httpGet-request an die Datenbank. Ist der Request erfolgreich
	 * ( Statuscode = 200), wird der gesamte Inhalt in einen String geschrieben
	 * und dieser zurückgegeben. Ansonsten wird ein leerer String zurückgegeben.
	 * 
	 * @param Pfad
	 * @return Dantenbank Request als String
	 * 
	 */
	private String httpGet(String path) throws ClientProtocolException,
			IOException {
		StringBuilder builder = new StringBuilder();
		HttpClient client = new DefaultHttpClient();
		HttpGet httpGet = new HttpGet(path);

		HttpResponse response = client.execute(httpGet);
		StatusLine statusLine = response.getStatusLine();
		int statusCode = statusLine.getStatusCode();
		if (statusCode == 200) {
			HttpEntity entity = response.getEntity();
			InputStream content = entity.getContent();
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					content));
			String line;
			while ((line = reader.readLine()) != null) {
				builder.append(line);
			}
			return builder.toString();
		} else {
			System.out.println("Failed to download file");
			return "";
		}
	}
}
